package com.jgzx.exception;


import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.jgzx.common.R;
import com.jgzx.common.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.ClassUtils;
import org.springframework.validation.BindException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.sql.DataTruncation;

/**
 * @author jackesy
 * @className: GlobalExceptionHandler
 * @description: 全局异常捕获处理
 * @create 2019/11/7 13:47
 **/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    @Value("${debug:false}")
    private boolean debug;

    /**
     * 处理业务处理的异常：400
     *
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(BizException.class)
    public R handleBizException(HttpServletRequest request, BizException e) {
        printExceptionLog(request, e);
        return R.fail(e.getCode(), e.getMessage());
    }

    /**
     * 处理请求参数校验的异常：401
     *
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler({MethodArgumentNotValidException.class, ServletRequestBindingException.class, TypeMismatchException.class, IllegalArgumentException.class,
            DataTruncation.class, JsonProcessingException.class, JSONException.class, NumberFormatException.class, BindException.class, MaxUploadSizeExceededException.class})
    public R handleMethodArgumentException(HttpServletRequest request, Exception e) {
        printExceptionLog(request, e);
        String exceptionName = ClassUtils.getShortName(e.getClass());
        if ("MethodArgumentNotValidException".equals(exceptionName)) {
            MethodArgumentNotValidException exception = (MethodArgumentNotValidException) e;
            return R.fail(ResultCode.ARGUMENT_VALID_ERROR, exception.getBindingResult().getAllErrors().get(0).getDefaultMessage());
        } else if("BindException".equals(exceptionName)){
            BindException exception = (BindException) e;
            return R.fail(ResultCode.ARGUMENT_VALID_ERROR, exception.getBindingResult().getAllErrors().get(0).getDefaultMessage());
        } else if ("ServletRequestBindingException".equals(exceptionName)) {
            return R.fail(ResultCode.ARGUMENT_VALID_ERROR, "请求缺少必要参数");
        } else if ("TypeMismatchException".equals(exceptionName)) {
            return R.fail(ResultCode.ARGUMENT_VALID_ERROR, "请求参数类型异常");
        } else if ("IllegalArgumentException".equals(exceptionName)) {
            return R.fail(ResultCode.ARGUMENT_VALID_ERROR, "请求参数非法异常");
        } else if ("DataTruncation".equals(exceptionName)) {
            return R.fail(ResultCode.ARGUMENT_VALID_ERROR, "请求参数太长,超出最大范围");
        } else if ("JsonProcessingException".equals(exceptionName) || "JSONException".equals(exceptionName)) {
            return R.fail(ResultCode.ARGUMENT_VALID_ERROR, "请求参数JSON格式错误");
        } else if ("NumberFormatException".equals(exceptionName)) {
            return R.fail(ResultCode.ARGUMENT_VALID_ERROR, "请求参数不为数字类型");
        } else if ("MaxUploadSizeExceededException".equals(exceptionName)) {
            return R.fail(ResultCode.ARGUMENT_VALID_ERROR, "上传的文件过大");
        }
        return R.fail(ResultCode.ARGUMENT_VALID_ERROR);
    }

    /**
     * 处理请求方法不支持的异常：405
     * POST、GET等方法
     *
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public R handleHttpRequestMethodNotSupportedException(HttpServletRequest request, HttpRequestMethodNotSupportedException e) {
        printExceptionLog(request, e);
        return R.fail(ResultCode.METHOD_NOT_SUPPORTED);
    }

    /**
     * 处理请求参数体不可读的异常：406
     *
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public R handleHttpMessageNotReadableException(HttpServletRequest request, HttpMessageNotReadableException e) {
        printExceptionLog(request, e);
        return R.fail(ResultCode.MSG_NOT_READABLE);
    }

    /**
     * 处理媒体类型不支持的请求异常：407
     * application/x-www-form-urlencoded,application/json
     *
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public R handleHttpMediaTypeNotSupportedException(HttpServletRequest request, HttpMediaTypeNotSupportedException e) {
        printExceptionLog(request, e);
        return R.fail(ResultCode.MEDIA_TYPE_NOT_SUPPORTED);
    }

    /**
     * 处理不支持编码的请求异常：408
     *
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(UnsupportedEncodingException.class)
    public R handleUnsupportedEncodingException(HttpServletRequest request, UnsupportedEncodingException e) {
        printExceptionLog(request, e);
        return R.fail(ResultCode.ENCODING_NOT_SUPPORTED);
    }

    /**
     * 处理FeignClient异常：410
     *
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(RuntimeException.class)
    public R handleClientException(HttpServletRequest request, RuntimeException e) {
        printExceptionLog(request, e);
        String msg = e.getMessage();
        //请求参数List校验，需在Controller类上加上@Validated注解
        if("ConstraintViolationException".equals(ClassUtils.getShortName(e.getClass()))){
            return R.fail(ResultCode.FAILURE,  StrUtil.sub(msg, msg.lastIndexOf(":"), msg.length()));
        }
        if(debug){
            return R.fail(ResultCode.INTERNAL_SERVER_ERROR, e.getMessage());
        }
        return R.fail(ResultCode.INTERNAL_SERVER_ERROR);
    }

    /**
     * 处理其他未知异常:500
     *
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    public R handleGlobalException(HttpServletRequest request, Exception e) {
        printExceptionLog(request, e);
        String msg = e.getMessage();
        if(e.getMessage().contains("ClientException")){
            String service_name = StrUtil.sub(msg, msg.lastIndexOf(":"), msg.length());
            return R.fail(ResultCode.FEIGN_CLIENT_EXCEPTION,  "微服务异常:" + service_name);
        }
        if(debug){
            return R.fail(ResultCode.INTERNAL_SERVER_ERROR, e.getMessage());
        }
        return R.fail(ResultCode.INTERNAL_SERVER_ERROR);
    }

    /**
     * 打印异常日志
     *
     * @param request
     * @param e
     */
    private void printExceptionLog(HttpServletRequest request, Exception e) {
        String requesrt_uri = request.getRequestURI();
        String packageName = ClassUtils.getPackageName(e.getClass());
        String exceptionName = ClassUtils.getShortName(e.getClass());
        log.error("请求资源：{},出现异常:{}", requesrt_uri, packageName + "." + exceptionName);
        log.error("异常日志：{}", ExceptionUtil.stacktraceToString(e));
    }


}
